/* Examples/jerasure_08.cpp
 * James S. Plank

Jerasure - A C/C++ Library for a Variety of Reed-Solomon and RAID-6 Erasure Coding Techniques
Copright (C) 2007 James S. Plank
  
James S. Plank
Department of Electrical Engineering and Computer Science
University of Tennessee 
Knoxville, TN 37996
plank@cs.utk.edu
*/

/*
 * $Revision: 1.2 $
 * $Date: 2008/08/19 17:41:40 $
 */
    

/*
	revised by:
  
  S. Simmerman
	2/25/08  

  Jerasure 2.0
  6/1/11

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jerasure-2.h"

#define talloc(type, num) (type *) malloc(sizeof(type)*(num))

void usage(char *s)
{
  fprintf(stderr, "usage: jerasure_08 k w - Example schedule cache usage with RAID-6\n");
  fprintf(stderr, "       \n");
  fprintf(stderr, "       m=2.  k+m must be <= 2^w.  It sets up a RAID-6 distribution matrix and encodes\n");
  fprintf(stderr, "       k sets of w*%ld bytes. It creates a schedule cache for decoding.\n", sizeof(uint64_t));
  fprintf(stderr, "       It demonstrates using the schedule cache for both encoding and decoding.\n");
  fprintf(stderr, "       Then it demonstrates using JER_Do_Parity() to re-encode the first.\n");
  fprintf(stderr, "       coding device\n");
  fprintf(stderr, "       \n");
  fprintf(stderr, "This demonstrates: JER_BM_To_Schedule_CSHR()\n");
  fprintf(stderr, "                   JER_RAID_6_Schedule_Cache()\n");
  fprintf(stderr, "                   JER_Schedule_Encode()\n");
  fprintf(stderr, "                   JER_Schedule_Decode_Cache()\n");
  fprintf(stderr, "                   JER_Free_Schedule_Cache()\n");
  fprintf(stderr, "                   JER_Get_Stats()\n");
  fprintf(stderr, "                   JER_Do_Parity()\n");
  if (s != NULL) fprintf(stderr, "%s\n", s);
  exit(1);
}

static void print_data_and_coding(JER_Slices * slices)
{
  int i, j, x, n, sp;
  uint64_t l;
  int k,m,w,psize;

  k = slices->K;
  m = slices->M;
  w = slices->W;
  psize = slices->PS;

  if(k > m) n = k;
  else n = m;
  //the coding column is placed sp spaces from the left
  //13 spaces are just for the labels and separators
  //2 spaces per byte so psize*2
  sp = psize*2 + 13;

  printf("%-*sCoding\n", sp, "Data");
  for(i = 0; i < n; i++) {
    for (j = 0; j < w; j++) {
      if(i < k) {
        if(j==0) printf("D%-2d p%-2d:", i,j);
        else printf("    p%-2d:", j);
        for(x = 0; x < psize; x +=sizeof(uint64_t)) {
          memcpy(&l, slices->ptrs[i]+j*psize+x, sizeof(uint64_t));
          printf(" %016lx", l);
        }
        printf("    ");
      }
      else printf("%*s", sp, "");
      if(i < m) {
        if(j==0) printf("C%-2d p%-2d:", i,j);
        else printf("    p%-2d:", j);
        for(x = 0; x < psize; x +=sizeof(uint64_t)) {
          memcpy(&l, slices->ptrs[i+k]+j*psize+x, sizeof(uint64_t));
          printf(" %016lx", l);
        }

      }
      printf("\n");
    }
  }

  printf("\n");
}


int main(int argc, char **argv)
{
  uint64_t l;
  int k, w, i, j, m;
  JER_Matrix *matrix;
  JER_Bitmatrix *bitmatrix;
  vector <JER_Schedule *> *cache;
  vector <int> erasures;
  vector <int> erased;
  vector <double> stats;
  JER_Schedule *smart;
  JER_Slices slice;

  if (argc != 3) usage(NULL);
  if (sscanf(argv[1], "%d", &k) == 0 || k <= 0) usage((char *)"Bad k");
  if (sscanf(argv[2], "%d", &w) == 0 || w <= 0 || w > 32) usage((char *)"Bad m");
  m = 2;
  if (w < 30 && (k+m) > (1 << w)) usage((char *)"k + m is too big");

  matrix = new JER_Matrix(m,k,w);
  //set top row = 1
  for (j = 0; j < k; j++) (matrix->Elts)[0][j] = 1;
  i = 1;
  //2nd row
  for (j = 0; j < k; j++) {
    (matrix->Elts)[1][j] = i;
    i = galois_single_multiply(i, 2, w);
  }
  bitmatrix = new JER_Bitmatrix(*matrix);

  smart = JER_BM_To_Schedule_CSHR(bitmatrix);

  cache = JER_RAID_6_Schedule_Cache(bitmatrix,1);

  srand48(0);

  //setup data and coding
  slice.ptrs.resize(k+m);
  slice.K = k;
  slice.M = m;
  slice.W = w;
  slice.PS = sizeof(uint64_t);
  slice.size = sizeof(uint64_t)*w;
  for (i = 0; i < k+m; i++) {
    slice.ptrs[i] = talloc(unsigned char, sizeof(uint64_t)*w);
    if(i<k){
      //add w random uint64's to data
      for (j = 0; j < w; j++) {
        l = (uint64_t)lrand48();
        l <<= 8*4;
        l += (uint64_t)lrand48();
        memcpy(slice.ptrs[i]+j*sizeof(uint64_t), &l, sizeof(uint64_t));
      }
    }else{
      //initialize the coding drives to all 0's
      bzero(slice.ptrs[i],sizeof(uint64_t)*w);
    }
  }

  //Show our original data
  printf("Original data and coding, before encoding:\n\n");
  print_data_and_coding(&slice);

  //Encode
  JER_Schedule_Encode(&slice,smart);

  //See how many XORs we performed
  stats.resize(3);
  JER_Get_Stats(stats);
  printf("Encoding Using Schedule Complete: - %.0lf XOR'd bytes\n\n", stats[0]);
  print_data_and_coding(&slice);

  //delete both coding drives
  erasures.resize(m);
  erasures[0] = k;
  erasures[1] = k+1;
  bzero(slice.ptrs[k],sizeof(uint64_t)*w);
  bzero(slice.ptrs[k+1],sizeof(uint64_t)*w);
  printf("Deleted two coding drives:\n\n");
  print_data_and_coding(&slice);

  //decode with the schedule cache
  JER_Schedule_Decode_Cache(&slice, *cache, erasures);
  JER_Get_Stats(stats);
  printf("Encoding Using Schedule Cache: - %.0lf XOR'd bytes\n\n", stats[0]);
  print_data_and_coding(&slice);

  //erase 2 random disks
  erased.resize(k+m,0);
  for (i = 0; i < m; ) {
    erasures[i] = lrand48()%(k+m);
    if (erased[erasures[i]] == 0) {
      erased[erasures[i]] = 1;
      bzero(slice.ptrs[erasures[i]],sizeof(uint64_t)*w);
      i++;
    }
  }
  printf("Erased %d random devices:\n\n", m);
  print_data_and_coding(&slice);

  //decode with cache
  JER_Schedule_Decode_Cache(&slice, *cache, erasures);
  JER_Get_Stats(stats);
  printf("Decoded Using Schedule Cache: - %.0lf XOR'd bytes\n\n", stats[0]);
  print_data_and_coding(&slice);

  //clear coding device 0
  bzero(slice.ptrs[k],sizeof(uint64_t)*w);

  //re-encode with jerasure_do_parity
  //the parity pointer is coding device 0
  JER_Do_Parity( &slice, slice.ptrs[k]);

  printf("State of the system after deleting coding device 0 and using\n");
  printf("JER_Do_Parity to re-encode it:\n\n");
  print_data_and_coding(&slice);

  delete smart;
  JER_Free_Schedule_Cache(cache);
  delete matrix;
  delete bitmatrix;

  printf("Smart schedule and cache freed\n\n");

  return 0;
}
